package com.powerunion.datacollection.report.excelreport.base.element;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.powerunion.datacollection.report.excelreport.Logger;
import com.powerunion.datacollection.report.excelreport.base.BaseBand;
import com.powerunion.datacollection.report.excelreport.base.BaseElement;
import com.powerunion.datacollection.report.excelreport.base.IFieldValueListener;
import com.powerunion.datacollection.report.excelreport.base.ITempletContainer;
import com.powerunion.datacollection.report.excelreport.base.Parameter;

import jxl.Cell;
import jxl.CellView;
import jxl.Range;
import jxl.write.WritableCell;
import jxl.write.WritableImage;
import jxl.write.WritableSheet;
import jxl.write.WriteException;
import jxl.write.biff.RowsExceededException;

/**
 * 容器类的实现，所有的模板定义都被抽象成一个个模板定义对象而放到容器中。
 * 
 * @author juny
 */
public class Templet extends BaseElement implements ITempletContainer {
    private static Logger log = Logger.getLogger(Templet.class);
    public Templet(ITempletContainer container) {
        super(container);
        elements = new HashMap();
    }
    /* 取得容器包含的模板元素列表
     * @see excel.report.util.ITempletContainer#getContainElements()
     */
    public List getContainElements() {
        return null;
    }
    
    /**
     * 取得顶层容器
     * @return
     */
    /*public ITempletContainer getTopContainer(){
        if(null != topContainer){
            return topContainer;
        }
        
        topContainer = this.getParentContainer();
        while(topContainer != null){
            if(topContainer instanceof BaseElement){
                topContainer = ((BaseElement)topContainer).getParentContainer();
            }else{
                break;
            }
        }
        
        if(null == topContainer){
            topContainer = this;
        }
        return topContainer;
    }*/
    //private ITempletContainer topContainer = null;

    /* 
     * @see excel.report.util.ITempletContainer#getElement(java.lang.String)
     */
    public BaseElement getElement(String name) {
        BaseElement element = null;
        Iterator ite = elements.entrySet().iterator();
        Entry entry = null;
        while(ite.hasNext()){
            entry = (Entry)ite.next();
            element = (BaseElement)entry.getValue();
            if(element instanceof BaseBand ){
                element = ((BaseBand)element).getElement(name);
            }
            if(null != element && element.getName().equals(name)){
                return element;
            }
        }
        return null;
    }
    
    public void iniElement() {
        BaseElement element = null;
        Iterator ite = elements.entrySet().iterator();
        Entry entry = null;
        while(ite.hasNext()){
            entry = (Entry)ite.next();
            element = (BaseElement)entry.getValue();
            //if(element instanceof ITempletContainer){
                element.iniElement();
            //}else if(element instanceof FormulateField){
            //    element.iniElement();
            //}
        }
    }
    
    /* 
     * @see excel.report.util.ITempletContainer#addElement(excel.report.util.BaseElement)
     */
    public void addElement(BaseElement element, int col, int row) {
        if(null != element){
            elements.put(getKey(col,row), element);
            element.setParentContainer(this);
        }
    }
    
    /**
     * 取得map键值
     * @param col
     * @param row
     * @return
     */
    private String getKey(int col, int row){
        StringBuffer key = new StringBuffer();
        key.append(col).append("#").append(row);
        return key.toString();
    }

    /* 
     * @see excel.report.util.ITempletContainer#getElement(int, int)
     */
    public BaseElement getElement(int col, int row) {
        return (BaseElement)elements.get(getKey(col, row));
    }

    /* 
     * @see excel.report.util.ITempletContainer#getBeginCol()
     */
    public int getBeginCol() {
        return this.beginCol;
    }

    /* 
     * @see excel.report.util.ITempletContainer#getBeginRow()
     */
    public int getBeginRow() {
        return this.beginRow;
    }

    /* 
     * @see excel.report.util.ITempletContainer#getEndCol()
     */
    public int getEndCol() {
        return this.endCol;
    }

    /* 
     * @see excel.report.util.ITempletContainer#getEndRow()
     */
    public int getEndRow() {
        return this.endRow;
    }
    
    /* (non-Javadoc)
     * @see excel.report.util.ITempletContainer#setBegin(int, int)
     */
    public void setBegin(int beginCol, int beginRow) {
        this.beginCol = beginCol;
        this.beginRow = beginRow;
    }
    
    /* (non-Javadoc)
     * @see excel.report.util.ITempletContainer#setEnd(int, int)
     */
    public void setEnd(int endCol, int endRow) {
        this.endCol = endCol;
        this.endRow = endRow;
    }
    
    /* (non-Javadoc)
     * @see excel.report.util.ITempletContainer#addMergedCell(jxl.Range)
     */
    public boolean addMergedCell(Range range) {
        Iterator itr = elements.entrySet().iterator();
        Entry entry = null;
        BaseElement element = null;
        while(itr.hasNext()){
            entry = (Entry)itr.next();
            element = (BaseElement)entry.getValue();
            if(null != element){
                if(element instanceof BaseBand){
                    if(((BaseBand)element).addMergedCell(range)){
                        return true;
                    }
                }
            }
        }
        
        //如果没有找到则将合并单元格信息保存在本模板中。
        int topRow = range.getTopLeft().getRow();
        int bottomRow = range.getBottomRight().getRow();
        
        //当定义模板带自身合并的单元格则忽略掉
        if(topRow == beginRow || bottomRow == endRow ||
                topRow == endRow || bottomRow == beginRow){
            if(log.isDebugEnable()){
                log.debug("Ignore merged cell:" + range.toString());
            }
            return true;
        }
        
        if(topRow > beginRow && bottomRow < endRow ){
            if(null == mergedCells){
                mergedCells = new ArrayList();
            }
            mergedCells.add(range);
            return true;
        }
        
        return false;
    }
    
    /*
     * (non-Javadoc)
     * @see excel.report.base.ITempletContainer#addImage(jxl.write.WritableImage)
     */
    public boolean addImage(WritableImage image) {
    	Iterator itr = elements.entrySet().iterator();
        Entry entry = null;
        BaseElement element = null;
        while(itr.hasNext()){
            entry = (Entry)itr.next();
            element = (BaseElement)entry.getValue();
            if(null != element){
                if(element instanceof BaseBand){
                    if(((BaseBand)element).addImage(image)){
                        return true;
                    }
                }
            }
        }
        
        //如果没有找到则将图片信息保存在本模板中。
        double topRow = image.getRow() - 1;
        double endRow = topRow + image.getHeight();
        if(topRow >= this.beginRow && endRow <= this.getEndRow()){
        	if(null == images){
        		images = new ArrayList();
        	}
        	images.add(image);
        	return true;
        }
        
        return false;
	}
    
    /*
     *  (non-Javadoc)
     * @see excel.report.base.ITempletContainer#getImages()
     */
    public List getImages() {
		return images;
	}
    
    /* (non-Javadoc)
     * @see excel.report.util.ITempletContainer#getMergedCells()
     */
    public List getMergedCells() {
        return mergedCells;
    }
    
    /*
     * (non-Javadoc)
     * @see excel.report.base.ITempletContainer#removeAllTempletImages()
     */
	public boolean removeAllTempletImages(WritableSheet sheet) {
        Iterator itr = elements.entrySet().iterator();
        Entry entry = null;
        BaseElement element = null;
        while(itr.hasNext()){
            entry = (Entry)itr.next();
            element = (BaseElement)entry.getValue();
            if(null != element){
                if(element instanceof BaseBand){
                    ((BaseBand)element).removeAllTempletImages(sheet);
                }
            }
        }
        if(null != images){
	        for(int i=0; i<images.size(); i++){
	            sheet.removeImage((WritableImage)images.get(i));
	        }
        }
        return true;
    }
    
    /*
     *  (non-Javadoc)
     * @see excel.report.base.ITempletContainer#write(excel.report.base.Parameter)
     */
	public boolean write(Parameter param) throws Exception {
        BaseElement element = null;
        
        int beginTempletRow = getBeginRow();
        int endTempletRow = getEndRow();
        
        //保存开始位置
        int bRow = param.curRow;
        
        for (int row = beginTempletRow + 1; row < endTempletRow; row++) {
            if((element = haveBaseBand(param, row)) != null){
                //如果有嵌套band或group
                element.write(param);
                row = ((BaseBand) element).getEndRow();
                //跳过band模板行
                continue;
            }else{
	            //创建一个新行
	            copyEmptyRow(param, row, param.curRow);
	            //根据模板行数据定义生成新行的数据
	            copyRow(param, row, param.curRow);
            }
            param.curRow++;
        }
        invokeListener();
        
        //拷贝合并单元格信息
        copyMergedCells(param, bRow);
        //拷贝图片信息
        copyImages(param, bRow);
        
        return true;
    }
    
    private void invokeListener() throws Exception{
        List list = getFieldValueListeners();
        if(null != list){
            for(int i=0; i<list.size(); i++){
                ((IFieldValueListener)list.get(i)).reset();
            }
        }
    }
    
    private BaseElement haveBaseBand(Parameter param, int curRow){
        int cells = param.sheet.getRow(curRow).length;
        BaseElement element = null;
        //
        for (int col = 0; col < cells; col++) {
            element = getElement(col, curRow);
            if (null != element) {
                if (element instanceof BaseBand) {
                    return element;
                }
            }
        }
        return null;
    }
    
    /**
     * 
     * @param param
     * @param sourceRow
     * @param destRow
     * @throws RowsExceededException
     */
    private void copyEmptyRow(Parameter param, int sourceRow, int destRow) 
				throws RowsExceededException{
		//拷贝行高度
		CellView rowView = param.sheet.getRowView(sourceRow);
		param.sheet.insertRow(destRow);
		param.sheet.setRowView(destRow, rowView.getSize());
	}
    
    /**
     * 拷贝一行数据，并将模板行定义的变量值写入到新行相应的位置
     * @param param
     * @param sourceRow
     * @param destRow
     * @throws Exception
     */
    private void copyRow(Parameter param, int sourceRow, int destRow) throws Exception{
        int cells = param.sheet.getColumns();
        BaseElement element = null;
        WritableCell newWc = null;
        WritableCell wc = null;
        //从模板拷贝行数据
        for (int col = 0; col < cells; col++) {
            wc = param.sheet.getWritableCell(col, sourceRow);
            element = getElement(col, sourceRow);
            if (null != element) {
                param.wc = wc;
                param.destCol = col;
                param.destRow = destRow;
                element.write(param);
                newWc = param.wc;
                param.wc = null;
            }else{
                newWc = wc.copyTo(col, destRow);
            }
            
            if(null != newWc){
                param.sheet.addCell(newWc);
                newWc = null;
            }
        }
    }
    
    /**
     * 拷贝组内合并单元格信息
     * @param param
     * @param bRow  开始行
     * @throws Exception
     */
    private void copyMergedCells(Parameter param,
				            int bRow)
    						throws Exception{
        
        //处理当前组.
        List mergedCells = getMergedCells();
        if(null == mergedCells){
            return;
        }
        
        Cell topLeft = null, bottonRight = null;
        int topLeftRow = 0, btRightRow = 0;
        BaseBand baseBand = null;
        int bandBeginRow = 0;
        
        for(int i=0; i<mergedCells.size(); i++){
            topLeft = ((Range)mergedCells.get(i)).getTopLeft();
            bottonRight = ((Range)mergedCells.get(i)).getBottomRight();
            //判断当前需合并的单元格之前是否存在嵌套的band，如果存在嵌套的band其
            //计算合并单元格的方法不一样，后者需要判断嵌套的band数据的结束位置，
            //并根据该结束位置加上模板中需合并的单元格与该band的逻辑位置来计算
            if((baseBand = getBaseBand(param, this.beginRow, topLeft.getRow())) == null){
                bandBeginRow = bRow;
                topLeftRow = bandBeginRow + (topLeft.getRow() - (this.beginRow + 1));
                btRightRow = bandBeginRow + (bottonRight.getRow() - (this.beginRow + 1));
            }else{
                bandBeginRow = baseBand.getRealBeginRow() + baseBand.getRealRowCount();
                topLeftRow = bandBeginRow + (topLeft.getRow() - (baseBand.getEndRow() + 1));
                btRightRow = bandBeginRow + (bottonRight.getRow() - (baseBand.getEndRow() + 1));
            }
            
            //debug info
            if(log.isDebugEnable()){
	            log.debug("merge cell beginrow:" + topLeftRow 
	                    				+ " begincol:" + topLeft.getColumn()
	                    				+ " endRow:" + btRightRow
	                    				+ " endCol:" + bottonRight.getColumn()
	                    				);
            }
            param.sheet.mergeCells(topLeft.getColumn(), 
                    topLeftRow,
                    bottonRight.getColumn(),
                    btRightRow);
        }
    }
    
    public void mergeCell(int beginRow, int beginCol,
    						int endRow, int endCol,
    						Parameter param
    						) throws Exception{
    	int topLeftRow = 0, btRightRow = 0;

    	topLeftRow = beginRow;
        btRightRow = endRow;
        
        //debug info
        if(log.isDebugEnable()){
            log.debug("merge cell beginrow:" + topLeftRow 
                    				+ " begincol:" + beginCol
                    				+ " endRow:" + btRightRow
                    				+ " endCol:" + endCol
                    				);
        }
        
        param.sheet.mergeCells(beginCol, 
                topLeftRow,
                endCol,
                btRightRow);
    }
    
    /**
     * 拷贝容器中的图片元素
     * @param param
     * @param bRow
     * @throws Exception
     */
    private void copyImages(Parameter param, int bRow) throws Exception {

        //处理当前容器中的图片.
        List imgs = this.getImages();
        if(null == imgs){
            return;
        }
        
        WritableImage image = null;
        WritableImage imgnew = null;
        double newTopLeftRow = 0.0;
        double leftRow = 0.0;
        BaseBand baseBand = null;
        int bandBeginRow = 0;
        //取得顶层模板容器所占行数
        ITempletContainer container = getTopContainer();
        
        int templetHeight = container.getEndRow() - container.getBeginRow() - 1;
        
        for(int i=0; i<imgs.size(); i++){
        	image = (WritableImage)imgs.get(i);
        	leftRow = image.getRow();
            //判断当前需合并的单元格之前是否存在嵌套的band，如果存在嵌套的band其
            //计算合并单元格的方法不一样，后者需要判断嵌套的band数据的结束位置，
            //并根据该结束位置加上模板中需合并的单元格与该band的逻辑位置来计算
            if((baseBand = getBaseBand(param, this.beginRow, (int)leftRow)) == null){
                bandBeginRow = bRow;
                newTopLeftRow = bandBeginRow + (leftRow - (this.beginRow + 1));
            }else{
                bandBeginRow = baseBand.getRealBeginRow() + baseBand.getRealRowCount();
                newTopLeftRow = bandBeginRow + (leftRow - (baseBand.getEndRow() + 1));
            }
            
            //拷贝图像的真实位置为删除模板后的位置，因为在删除模板时，整个Excel报表文件的行数会发生改变，
            //这时而图片的位置不会自动改变，所以在这里拷贝图像时，图像的实际位置因为删除模板后的位置。
            //log.debug(newTopLeftRow + " " + templetHeight);
            newTopLeftRow -= templetHeight;
            
            //debug info
            if(log.isDebugEnable()){
	            log.debug("copy images beginrow:" + newTopLeftRow 
	                    				+ " begincol:" + image.getColumn()
	                    				+ " endRow:" + newTopLeftRow + image.getHeight()
	                    				+ " endCol:" + image.getColumn() + image.getWidth()
	                    				);
            }
            
            imgnew = new WritableImage(image.getColumn(),
            		newTopLeftRow,
                    image.getWidth(),
                    image.getHeight(),
                    image.getImageData());
            param.sheet.addImage(imgnew);
        }
	}
    
    /**
     * 取得顶层容器
     * @return
     */
    public ITempletContainer getTopContainer(){
        ITempletContainer top = super.getTopContainer();
        //如果自己本身就是顶层容器，则返回自己
        if(null == top){
            return this;
        }
        return top;
    }
    
    /**
     * 取得从结束位置endRow开始到开始位置间最靠近结束位置的BaseBand
     * @param param 
     * @param beginRow
     * @param endRow
     * @return
     */
    private BaseBand getBaseBand(Parameter param, int beginRow, int endRow){
        BaseElement element = null;
        int cols = param.sheet.getColumns();
        for(int row=endRow; row>=beginRow; row--){
            for(int col=0; col<cols; col++){
                element = this.getElement(col, row);
                if(element instanceof BaseBand){
                    return (BaseBand)element;
                }
            }
        }
        return null;
    }
    
    public String toString(){
        return "beginRow=" + beginRow + " beginCol=" + beginCol + 
        		" endRow=" + endRow + " endCol=" + endCol + " \n" +
        		this.elements.toString();
    }
    
    private int beginRow = 0;
    private int beginCol = 0;
    private int endRow = 0;
    private int endCol = 0;
    private Map elements = null;
    private List mergedCells = null;
    private List images = null;
    //private ITempletContainer parentContainer = null;
}